<?php

declare(strict_types=1);
/**
 * This file is part of habo_new_media.
 *
 * @link     https://www.berlen.cn/
 * @contact  tech@berlen.cn
 * @license  http://gitlab.rehou.cn/habo/habo_new_media/-/blob/master/README.md
 */

namespace app\service\notice\feishu;

use app\enum\FsEnum;
use app\enum\FsUrlEnum;
use app\enum\NoticeEnum;
use app\model\StaffFsAccessTokenModel;
use app\service\BaseService;
use app\service\notice\NoticeInterface;
use app\service\notice\NoticeLogTrait;
use proxy\Client;
use think\Exception;
use think\facade\Cache;

final class FeiShuService extends BaseService implements NoticeInterface
{
    use NoticeLogTrait;

    public const APP_ID = 'cli_a1cf3e238d39d00c';

    public const APP_SECRET = 'KZJPX4B8TboiuFQKvvQ0WgWmJiFbC6wb';

    /**
     * @var array 请求参数
     */
    protected array $req = [];

    /**
     * @var array 应答参数
     */
    protected array $rep = [];

    /**
     * @var array 接收人
     */
    protected array $staffs = [];

    /**
     * @var int 通知类型
     */
    protected int $noticeType = NoticeEnum::NOTICE_TYPE_FS;

    /**
     * @var array 配置
     */
    private array $conf = [];

    /**
     * @var string token
     */
    private string $token = '';

    /**
     * @param array $conf 配置
     * @throws Exception
     */
    public function __construct(array $conf = [])
    {
        $this->setConf($conf);
        $this->setToken();
    }

    /**
     * 获取 tenant_access_token（企业自建应用）.
     * @return bool|mixed|string
     * @throws Exception
     */
    public function getTenantAccessToken()
    {
        $redis = Cache::store('redis')->handler();
        $params = [
            'app_id' => self::APP_ID,
            'app_secret' => self::APP_SECRET,
        ];
        $client = new Client();
        $headers = $this->getHeader();
        $serviceName = FsUrlEnum::getServiceName('feishu');
        $serviceUrl = FsUrlEnum::getServiceUrl('token', 'tenant_access_token');
        $data = $client->postJson($serviceName, $serviceUrl, $params)->setHeader($headers)->exec(Client::RETURN_TYPE);
        if ($data && $data['code'] == 0) {
            $access_token = $data['tenant_access_token'];
            $expire = $data['expire'] - 120;
            $redis->set(FsEnum::TOKEN_KEY, $access_token, $expire);
            return $data['tenant_access_token'];
        }
        return '';
    }

    /**
     * 批量发送消息.
     * @param array $params 请求参数
     * @return bool
     * @throws Exception
     */
    public function sendBatchMessage(array $params = []): bool
    {
        $this->req = $this->getParam($params);
        $headers = $this->getHeader();
        $client = new Client();
        $serviceName = FsUrlEnum::getServiceName('feishu');
        $serviceUrl = FsUrlEnum::getServiceUrl('msg', 'batch_msg');
        $this->rep = $client->postJson($serviceName, $serviceUrl, $this->req)->setHeader($headers)->exec(Client::RETURN_TYPE);
        if (! $this->setNoticeLog()) {
            return false;
        }
        return $this->isSendSuccess() || self::setError($res['msg'] ?? '发送失败');
    }

    /**
     * 发送单个消息.
     * @param array $params 请求参数
     * @return bool
     * @throws Exception
     */
    public function sendSingleMessage(array $params = []): bool
    {
        $this->req = $this->getParam($params);
        $headers = $this->getHeader();
        $client = new Client();
        $serviceName = FsUrlEnum::getServiceName('feishu');
        $serviceUrl = FsUrlEnum::getServiceUrl('msg', 'single_msg');
        $this->rep = $client->postJson($serviceName, $serviceUrl, $this->req)->setHeader($headers)->exec(Client::RETURN_TYPE);
        if (! $this->setNoticeLog()) {
            return false;
        }
        return $this->isSendSuccess() || self::setError($res['msg'] ?? '发送失败');
    }

    /**
     * 添加公共参数.
     * @param array $params 请求参数
     * @return array
     */
    public function getParam(array $params = []): array
    {
        return $params;
    }

    /**
     * 设置配置.
     * @param array $conf 配置
     * @return bool
     */
    public function setConf(array $conf = []): bool
    {
        $this->conf = $conf;
        return true;
    }

    /**
     * 是否发送成功.
     */
    public function isSendSuccess(): bool
    {
        return isset($this->rep['code']) && $this->rep['code'] == 0;
    }

    /**
     * 批量发送消息.
     *
     * @param array $staffIds 员工id
     * @param string $msg 消息内容
     * @param array $param 接口参数
     * @throws Exception
     */
    public function sendStaffMsg(array $staffIds = [], string $msg = '', array $param = []): bool
    {
        $openIds = (new StaffFsAccessTokenModel())->whereIn('staff_id', $staffIds)->column('open_id');
        if (! $openIds) {
            return self::setError('获取用户平台id失败');
        }
        $param = [
            'open_ids' => $openIds,
            'receive_id' => $param['receive_id'] ?? 'open_id',
            'msg_type' => $param['msg_type'] ?? 'text',
            'content' => [
                'text' => $msg,
            ],
        ];
        return $this->sendBatchMessage($param);
    }

    /**
     * 设置header.
     * @return string[]
     */
    private function getHeader(): array
    {
        return [
            'Authorization' => 'Bearer ' . $this->token,
            'Content-Type' => 'application/json; charset=utf-8',
        ];
    }

    /**
     * 设置token方法.
     * @throws Exception
     */
    private function setToken(): void
    {
        $redis = Cache::store('redis')->handler();
        $redisKey = $redis->get(FsEnum::TOKEN_KEY);
        if (! $redisKey) {
            $this->token = self::getTenantAccessToken();
        }
        $this->token = $redis->get(FsEnum::TOKEN_KEY);
    }
}
